﻿using System;
using System.Reflection;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;

namespace MugenInjection.Parameters
{
    /// <summary>
    /// Represents the value of a method parameter.
    /// </summary>
    public class MethodParameter : InjectionParameter
    {
        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, string name, ParameterDelegate<object> getValueDelegate, Type type = null)
            : base(MemberTypes.Method, name, getValueDelegate)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            MethodName = methodName;
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            ParameterType = type;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, string name, object value)
            : base(MemberTypes.Method, name, value)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            MethodName = methodName;
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, string name, Type parameterType, object value)
            : base(MemberTypes.Method, name, value)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            Validate.ArgumentNotNull(parameterType, "parameterType");
            MethodName = methodName;
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            ParameterType = parameterType;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, int parameterPosition, ParameterDelegate<object> getValueDelegate, Type type = null)
            : base(MemberTypes.Method, EmptyParameterName, getValueDelegate)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            MethodName = methodName;
            Position = parameterPosition;
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            ParameterType = type;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, int parameterPosition, object value)
            : base(MemberTypes.Method, EmptyParameterName, value)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            MethodName = methodName;
            Position = parameterPosition;
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="MethodParameter" /> class.
        /// </summary>
        public MethodParameter(string methodName, int parameterPosition, Type parameterType, object value)
            : base(MemberTypes.Method, EmptyParameterName, value)
        {
            Validate.ArgumentNotNullOrEmpty(methodName, "methodName");
            Validate.ArgumentNotNull(parameterType, "parameterType");
            MethodName = methodName;
            Position = parameterPosition;
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            ParameterType = parameterType;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the method name.
        /// </summary>
        public string MethodName { get; private set; }

        /// <summary>
        /// Gets the position of parameter, if any.
        /// </summary>
        public int? Position { get; private set; }

        #endregion

        #region Override

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public override bool Equals(IInjectionParameter other)
        {
            return base.Equals(other) && ((MethodParameter)other).MethodName.Equals(MethodName);
        }

        /// <summary>
        /// Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="memberInfo">The specified <see cref="MemberInfo"/></param>
        /// <param name="parameter">The specified <see cref="ParameterInfo"/> </param>
        /// <returns><c>True</c> if the specified request has been resolved; otherwise, <c>false</c>.</returns>
        protected override bool CanResolveInternal(MemberInfo memberInfo, ParameterInfo parameter)
        {
            if (parameter == null || !MethodName.Equals(memberInfo.Name) || !IsAssignableParameter(parameter.ParameterType))
                return false;
            var position = Position;
            if (position.HasValue)
                return parameter.Position == position.Value;
            return parameter.Name.Equals(Name);
        }

        #endregion
    }
}